package net.lesscoding.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import net.lesscoding.common.CommonConst;
import net.lesscoding.common.Result;
import net.lesscoding.common.ResultFactory;
import net.lesscoding.common.TableInfo;
import net.lesscoding.config.GenerateConfig;
import net.lesscoding.config.TemplateConfig;
import net.lesscoding.entity.OutPathConfig;
import net.lesscoding.mapper.OutPathConfigMapper;
import net.lesscoding.parse.MysqlParser;
import net.lesscoding.parse.OracleParser;
import net.lesscoding.service.GenerateService;
import net.lesscoding.utils.GeneratorUtil;
import net.lesscoding.utils.ParseUtil;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

/**
 * @author eleven
 * @date 2022/7/14 11:21
 * @description
 */
@Slf4j
@Service
public class GenerateServiceImpl implements GenerateService {
    /**
     * 解析json字符串
     */
    private static Gson gson = new Gson();

    private ParseUtil sqlParser;
    /**
     * 默认的文件名为resources.zip
     */
    private String filename = "resources.zip";

    private String pathName = "";

    @Autowired
    private OutPathConfigMapper outPathConfigMapper;

    /**
     * 从sql文件解析并生成代码
     * @param file              传入过来的文件
     * @param configJson        配置json字符串
     * @param response          客户端响应
     */
    @Override
    public void doGenerate(MultipartFile file, String configJson, HttpServletResponse response) {
        //从json字符串中解析出来模板
        TemplateConfig templateConfig = gson.fromJson(configJson, TemplateConfig.class);
        List<TableInfo> tableInfoList = null;
        //如果文件不为空的话的就解析数据
        if(file != null) {
            tableInfoList = parseSqlInputStream(file, templateConfig);
            String originalFilename = file.getOriginalFilename();
            int index = originalFilename.lastIndexOf(".");
            originalFilename = originalFilename.substring(0,index);
            pathName = originalFilename;
            filename = originalFilename + ".zip";
        }
        if(CollUtil.isEmpty(tableInfoList)){
            throw new RuntimeException("解析Sql失败，请联系系统管理员");
        }
        templateConfig.setTableList(tableInfoList);
        //执行生成代码逻辑
        doGenerate(templateConfig,response);
    }

    /**
     * 解析sql文件返回解析好的表对象
     * @param file          要解析的sql文件
     * @param configJson    选择的的配置
     * @return Result 通用返回类
     */
    @Override
    public Result parseSql(MultipartFile file, String configJson) {
        TemplateConfig templateConfig = gson.fromJson(configJson, TemplateConfig.class);
        List<TableInfo> tableInfoList = parseSqlInputStream(file, templateConfig);
        return ResultFactory.success(tableInfoList);
    }

    @Override
    public void doGenerate(TemplateConfig config, HttpServletResponse response) {
        response.setHeader("Content-Disposition", "attachment;filename="+ URLUtil.encode(filename, Charset.forName("utf-8")));
        response.setHeader("Connection", "close");
        response.setHeader("Content-Type", "application/octet-stream");

        QueryWrapper<OutPathConfig> wrapper = new QueryWrapper<>();
        wrapper.eq("del_flag",false);
        List<OutPathConfig> configList = outPathConfigMapper.selectList(wrapper);

        GenerateConfig generateConfig = config.getGenerateConfig();
        try(ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
            List<VelocityContext> contextList = getContextList(config);
            if(generateConfig.getEnableEntity()){
                generateCurdCode(zos,contextList,configList,"curd","entity");
            }
            if(generateConfig.getEnableController()){
                generateCurdCode(zos,contextList,configList,"curd","controller");
            }
            if(generateConfig.getEnableService()){
                generateCurdCode(zos,contextList,configList,"curd","service");
            }
            if(generateConfig.getEnableServiceImpl()){
                generateCurdCode(zos,contextList,configList,"curd","impl");
            }
            if(generateConfig.getEnableMapper()){
                generateCurdCode(zos,contextList,configList,"curd","mapper");
            }
            if(generateConfig.getEnableXml()){
                generateCurdCode(zos,contextList,configList,"curd","mapper.xml");
            }
            if(generateConfig.getEnableBase()){
                generateCurdCode(zos,contextList,configList,"base",null);
                generateCurdCode(zos,contextList,configList,"utils",null);
                generateCurdCode(zos,contextList,configList,"config",null);
                generateCurdCode(zos,contextList,configList,"common",null);
            }
            if(generateConfig.getEnableVue()){
                generateCurdCode(zos,contextList,configList,"vue",null);
                generateCurdCode(zos,contextList,configList,"components",null);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 生成CURD代码
     * @param zos                   压缩包输出流
     * @param contextList           context集合
     * @param configList            配置列表
     * @param model                 第一类型
     * @param secondModel           第二类型
     */
    private void generateCurdCode(ZipOutputStream zos,List<VelocityContext> contextList, List<OutPathConfig> configList, String model, String secondModel) {
        if(StrUtil.isNotBlank(secondModel)){
            log.info("======开始生成{}代码=====",secondModel);
            OutPathConfig outPathConfig = getSecondModel(configList,secondModel);
            generateByConfig(zos, contextList,outPathConfig);
            log.info("============={}生成完成=============",secondModel);
        }else{
            List<OutPathConfig> outPathConfigList = getFirstModel(configList, model);
            log.info("======开始生成{}代码=====",model);
            for (OutPathConfig outPathConfig : outPathConfigList) {
                generateByConfig(zos, contextList, outPathConfig);
            }
            log.info("============={}生成完成=============",model);
        }

    }

    /**
     *
     * @param zos
     * @param contextList
     * @param outPathConfig
     */
    private void generateByConfig(ZipOutputStream zos, List<VelocityContext> contextList, OutPathConfig outPathConfig) {
        String path = "";
        Template template = null;
        StringWriter writer = null;
        ByteArrayInputStream bais = null;
        for (VelocityContext context : contextList) {
            writer = new StringWriter();
            template = GeneratorUtil.buildTemplate(outPathConfig.getTempPath());
            template.merge(context,writer);
            bais = new ByteArrayInputStream(writer.toString().getBytes());
            TableInfo tableInfo = (TableInfo) context.get("tableInfo");
            //将Writer转换成InputStream
            path = GeneratorUtil.getPath(outPathConfig,pathName,context);
            log.info("=====当前生成文件为{}=====",path);
            GeneratorUtil.writeToZipOutStream(zos,bais,path);
            log.info("=====生成{}成功=====",path);
        }
    }

    /**
     *
     * @param list
     * @param model
     * @return
     */
    private List<OutPathConfig> getFirstModel(List<OutPathConfig> list,String model){
        return list.stream()
                .filter(item -> model.equals(item.getModel()))
                .collect(Collectors.toList());
    }

    private OutPathConfig getSecondModel(List<OutPathConfig> list,String secondModel){
        return list.stream()
                .filter(item -> secondModel.equals(item.getSecondModel()))
                .collect(Collectors.toList())
                .get(0);
    }

    /**
     * 生成实体类文件
     * @param templateConfig    模板配置选择想
     */
    private List<VelocityContext> getContextList(TemplateConfig templateConfig){
        Map<String, Object> contextMap = null;
        List<VelocityContext> resultList = new ArrayList<>();
        VelocityContext context = null;
        for (TableInfo tableInfo : templateConfig.getTableList()) {
            contextMap = GeneratorUtil.commonMap(templateConfig);
            contextMap.put("tableInfo",tableInfo);
            contextMap.put("columnList",tableInfo.getColumnList());
            contextMap.put("commonColumnList",templateConfig.getCommonProperties());
            contextMap.put("database",templateConfig.getDatabase());
            context = new VelocityContext(contextMap);
            resultList.add(context);
        }
        return resultList;
    }


    /**
     * 解析Sql文件流
     * @param file              前端传入的sql文件流
     * @param templateConfig    用户选择的配置信息
     * @return List             返回解析好的表格信息
     */
    private List<TableInfo> parseSqlInputStream(MultipartFile file,TemplateConfig templateConfig){
        try(InputStream inputStream = file.getInputStream()) {
            String sql = GeneratorUtil.inputStreamToString(inputStream);
            if( StrUtil.equalsIgnoreCase(templateConfig.getDatabaseType(),CommonConst.MYSQL) || sql.contains(CommonConst.MYSQL)){
                sqlParser = new MysqlParser(templateConfig.getUserChoose());
            }else if(StrUtil.equalsIgnoreCase(templateConfig.getDatabaseType(),CommonConst.ORACLE) || sql.contains(CommonConst.ORACLE)){
                sqlParser = new OracleParser(templateConfig.getUserChoose());
            }
            return sqlParser.parseExportSql(sql);
        } catch (IOException e) {
            log.error("获取sql文件流失败",e);
        }
        return null;
    }

}
